/*****************************************************************************
 *   Copyright 2011 ZHUANG Yuan              	                             *
 *   SudokuITS as Final Year Project for BScCS, City University of Hong Kong *
 *                                      									 *
 *   Licensed under the Apache License, Version 2.0 (the "License");         *
 *   you may not use this file except in compliance with the License.        *
 *   You may obtain a copy of the License at                                 *
 *                                                                           *
 *   http://www.apache.org/licenses/LICENSE-2.0                              *
 *                                                                           *
 *   Unless required by applicable law or agreed to in writing, software     *
 *   distributed under the License is distributed on an "AS IS" BASIS,       *
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.*
 *   See the License for the specific language governing permissions and     *
 *   limitations under the License.                                          *
 *****************************************************************************/

package android.sudoku.its.tutor;

import java.util.ArrayList;
import java.util.List;

import android.content.Context;
import android.sudoku.its.expert.Expert;
import android.sudoku.its.student.Student;
import android.sudoku.its.util.GameVariables;
import android.sudoku.its.views.GameBoard;
import android.sudoku.its.views.GamePage;

public class Tutor {	
	private InstructionGenerator instrGenerator;	
	// Store the index of prioritized strategy
	private int prioStrategy;	
	// Store the number of times that hint button is clicked
	private int hintClickCount;
	// An array of length 81 indicating if Naked Pair strategy of a certain cell has been taught to the student
	// 0 - haven't taught or Naked Pair not applicable; 1 - taught
	int[] nakedPairTaughtMatrix = new int[GameVariables.PUZZLE_LENGTH];
	// An array of length 81 indicating if Hidden Pair strategy of a certain cell has been taught to the student
	// 0 - haven't taught or Naked Pair not applicable; 1 - taught
	int[] hiddenPairTaughtMatrix = new int[GameVariables.PUZZLE_LENGTH];

	public Tutor() {        
        // Initialize nakedPairTaughtMatrix
        for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i ++)
        	nakedPairTaughtMatrix[i] = 0;
        // Initialize hiddenPairTaughtMatrix
        for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i ++)
        	hiddenPairTaughtMatrix[i] = 0;
	}
	
	public Tutor(Context context, String langPref, int prioStrategy, int studentLevel) {
        this.prioStrategy = prioStrategy;      
        hintClickCount = 0;   
        // Initialize nakedPairTaughtMatrix
        for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i ++)
        	nakedPairTaughtMatrix[i] = 0;
        // Initialize hiddenPairTaughtMatrix
        for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i ++)
        	hiddenPairTaughtMatrix[i] = 0;
        // Initialize instruction generator
        instrGenerator = new InstructionGenerator(context, langPref);
	}

	/**
	 * Match student used strategy by checking applicable strategies of the target cell and update student model
	 * @param nRow			row index of the cell received student valid input
	 * @param nCol			column index of the cell received student valid input
	 * @param expert		current expert model
	 * @param student		current student model
	 */
	public void matchStudentUsedStrategy(int nRow, int nCol, Expert expert, Student student) {
		// Get applicable strategies of the target cell
		List<Integer> applicableStrategy = new ArrayList<Integer>();
		applicableStrategy = expert.getApplicableStrategyForCell(nRow, nCol);

		// If there are multiple strategies applicable to the target cell
		// DO NOT update student model, because we are not sure which one the student has applied.
		// If not, update student model	
		if (applicableStrategy.size() == 1){ // there is one applicable strategy
			int strategy = applicableStrategy.get(0);
			// Update student model
			switch(strategy) {
				case GameVariables.FULL_HOUSE_INDEX:
					student.updateMasteryOnFullHouse();
					break;
				case GameVariables.NAKED_SINGLE_INDEX:
					student.updateMasteryOnNakedSingle();
					break;
				case GameVariables.HIDDEN_SINGLE_INDEX:
					student.updateMasteryOnHiddenSingle();
					break;
				case GameVariables.NAKED_PAIR_INDEX:
					student.updateMasteryOnNakedPair();
					break;
				case GameVariables.HIDDEN_PAIR_INDEX:
					student.updateMasteryOnHiddenPair();
					break;
				case GameVariables.LOCKED_CANDIDATE_INDEX:
					student.updateMasteryOnLockedCandidate();
					break;
			}
		}		
	}
	
	public String giveHint(Context context, int[][] clues) {		
		Student student = ((GamePage) context).getStudent();
		Expert expert = ((GamePage) context).getExpert();
		int studentLevel = student.getLevel();			
		List<List<Integer>> asm = expert.getApplicableStrategyMatrix();
		List<List<Integer>> cm = expert.getCandidateMatrix();
		
		String instrMsg = "\n\nCan you try to solve some cells by yourself before requiring hints?";
		
		// Check if there is any cell that can apply prioritized strategy
		for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i++) {
			if ((!asm.get(i).isEmpty()) && asm.get(i).contains(prioStrategy)) { // if there is applicable strategy in this cell
				if (prioStrategy == GameVariables.NAKED_PAIR_INDEX) { // if the strategy to be taught is Naked Pair
					if (nakedPairTaughtMatrix[i] == 0)  // Naked Pair has not been taught
						return giveHintFunc(context, studentLevel, prioStrategy, i, clues, cm);
				}
				else if (prioStrategy == GameVariables.HIDDEN_PAIR_INDEX) { // if the strategy to be taught is Hidden Pair
					if (hiddenPairTaughtMatrix[i] == 0) // Hidden Pair has not been taught
						return giveHintFunc(context, studentLevel, prioStrategy, i, clues, cm); 
				}
				else // if the strategy to be taught is among other strategies
					return giveHintFunc(context, studentLevel, prioStrategy, i, clues, cm);		
			}
		}
		
		// There is no place to apply prioritized strategy, return the first applicable strategy
		for (int i = 0; i < GameVariables.PUZZLE_LENGTH; i++) {
			if (!asm.get(i).isEmpty()) {
				int strategy = asm.get(i).get(0);
				if (strategy == GameVariables.NAKED_PAIR_INDEX) { // if the strategy to be taught is Naked Pair
					if (nakedPairTaughtMatrix[i] == 0)
						return giveHintFunc(context, studentLevel, strategy, i, clues, cm);
				}
				else if (strategy == GameVariables.HIDDEN_PAIR_INDEX) { // if the strategy to be taught is Hidden Pair
					if (hiddenPairTaughtMatrix[i] == 0) // Hidden Pair has not been taught
						return giveHintFunc(context, studentLevel, strategy, i, clues, cm); 
				}
				else // if the strategy to be taught is not Naked Pair
					return giveHintFunc(context, studentLevel, strategy, i, clues, cm);				
			}
		}

		// If there is no strategy applicable for the whole puzzle
		return instrMsg;		
	}
	
	String giveHintFunc(Context context, int studentLevel, int strategyIndex, int cellIndex, int[][] clues, List<List<Integer>> cm) {
		int nRow = cellIndex/GameVariables.HOUSE_SIZE;
		int nCol = cellIndex%GameVariables.HOUSE_SIZE;
		hintClickCount++;
		
		// Focus on the cell to be explained
		GameBoard gameBoard = ((GamePage) context).getGameBoard();
		gameBoard.setNRow(nRow);
		gameBoard.setNCol(nCol);
		gameBoard.selectACell();
		
		if (studentLevel == GameVariables.LEVEL_ADVANCED) {// for advanced level student
			if (hintClickCount == 1)
				return instrGenerator.giveHighLevelInstr(strategyIndex, nRow, nCol);
			else if (hintClickCount == 2)
				return instrGenerator.giveMiddleLevelInstr(strategyIndex, nRow, nCol);
			else if (hintClickCount == 3)
				return instrGenerator.giveLowLevelInstr(context, strategyIndex, nRow, nCol, clues);			
			else {	// if hintClickCount exceeded 3, give answer directly				
				// Set hint click count to zero
				setHintClickCountToZero();
				return instrGenerator.giveAnswer(context, strategyIndex, nRow, nCol, clues, this);
			}
		}
		else if (studentLevel == GameVariables.LEVEL_INTERMEDIATE) {// for intermediate level student
			if (hintClickCount == 1)
				return instrGenerator.giveMiddleLevelInstr(strategyIndex, nRow, nCol);
			else if (hintClickCount == 2) 
				return instrGenerator.giveLowLevelInstr(context, strategyIndex, nRow, nCol, clues);			
			else {	// if hintClickCount exceeded 2, give answer directly		
				// Set hint click count to zero
				setHintClickCountToZero();
				return instrGenerator.giveAnswer(context, strategyIndex, nRow, nCol, clues, this);
			}
		}
		else {	// for beginner level student
			if (hintClickCount == 1)
				return instrGenerator.giveLowLevelInstr(context, strategyIndex, nRow, nCol, clues);
			else {	// if hintClickCount exceeded 1, give answer directly
				// Set hint click count to zero
				setHintClickCountToZero();
				return instrGenerator.giveAnswer(context, strategyIndex, nRow, nCol, clues, this);
			}
		}
	}
	
	/**
	 * Get instruction for valid number assignment
	 * @param num			Number input by user
	 * @param nRow			Row index of the cell received user input
	 * @param nCol			Column index of the cell received user input
	 * @return				A string containing instructions to be given to the user
	 */
	public String getInstrForValidAsg(int num, int nRow, int nCol) {
		return instrGenerator.giveInstrForValidAsg(num, nRow, nCol);
	}

	/**
	 * Get instruction for invalid number assignment
	 * @param num			Number input by user
	 * @param nRow			Row index of the cell received user input
	 * @param nCol			Column index of the cell received user input
	 * @return				A string containing instructions to be given to the user
	 */
	public String getInstrForInvalidAsg(int num, int nRow, int nCol) {
		return instrGenerator.giveInstrForInvalidAsg(num, nRow, nCol);
	}		
	
	/**
	 * Set hint click count to zero so that next time the user request hint, 
	 * the instruction will be provided from the most original level 
	 */
	public void setHintClickCountToZero() {
		hintClickCount = 0;
	}
	
	protected void updateNakedPairTaughtMatrix(int[] indexes) {
		for (int i = 0; i < indexes.length; i ++) 
			this.nakedPairTaughtMatrix[indexes[i]] = 1;
		
	}
	
	protected void updateHiddenPairTaughtMatrix(int[] indexes) {
		for (int i = 0; i < indexes.length; i ++) 
			this.hiddenPairTaughtMatrix[indexes[i]] = 1;		
	}
}
